/*
 *	Fraction calculator
 *
 *	Copyright (C) 2011 XiaoJSoft Studio. All Rights Reserved.
 *	Copyright (C) Ji WenCong <whs_jwc@163.com>
 *
 *	This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdio.h>
#include <malloc.h>
#include "../include/mmdiv.h"
#include "../include/fraction.h"

/*
 *	fact fractionCreate(long double numer, long double denomin);
 *
 *	Creates a new fraction.
 *
 *	numer	->	the numerator,
 *	denomin	->	the denominator.
 */
fact fractionCreate(long double numer, long double denomin)
{
	fact new_fact;

	new_fact.numerator = numer;

	if (numer == 0) {
		new_fact.denominator = 1;
	} else {
		new_fact.denominator = denomin;
	}

	return(new_fact);
}

/*
 *	fact fractionSimplify(fact input);
 *
 *	Simplifies a fraction.
 *
 *	input	->	the fraction.
 */
fact fractionSimplify(fact input)
{
	long double ld_mdiv;
	fact frNew;

	ld_mdiv = mdiv(input.numerator, input.denominator);

	frNew.numerator = input.numerator / ld_mdiv;
	frNew.denominator = input.denominator / ld_mdiv;

	if (frNew.numerator == 0) {
		frNew.denominator = 1;
	}

	if (frNew.denominator == 0) {
		frNew.numerator = 0;
		frNew.denominator = 1;
	}

	return(frNew);
}

/*
 *	fact fractionPlus(fact frNum1, fact frNum2);
 *
 *	To get the sum of two fractions (frNum1 & frNum2).
 *
 *	frNum1	->	Fraction one,
 *	frNum2	->	Fraction two.
 */
fact fractionPlus(fact frNum1, fact frNum2)
{
	fact frRet;
	fact frinNum1, frinNum2;

	frinNum1 = frNum1;
	frinNum2 = frNum2;

	if (frinNum1.numerator == 0) {
		frinNum1.denominator = 1;
	}

	if (frinNum2.numerator == 0) {
		frinNum2.denominator = 1;
	}

	frRet = fractionCreate(frinNum1.numerator * frinNum2.denominator + frinNum2.numerator * frinNum1.denominator, frinNum1.denominator * frinNum2.denominator);
	frRet = fractionSimplify(frRet);

	return(frRet);
}

/*
 *	fact fractionMinus(fact frNum1, fact frNum2);
 *
 *	To get the subtract of two fractions (frNum1 & frNum2).
 *
 *	frNum1	->	Fraction one,
 *	frNum2	->	Fraction two.
 */
fact fractionMinus(fact frNum1, fact frNum2)
{
	fact frRet;
	fact frinNum1, frinNum2;

	frinNum1 = frNum1;
	frinNum2 = frNum2;

	if (frinNum1.numerator == 0) {
		frinNum1.denominator = 1;
	}

	if (frinNum2.numerator == 0) {
		frinNum2.denominator = 1;
	}

	frRet = fractionCreate(frinNum1.numerator * frinNum2.denominator - frinNum2.numerator * frinNum1.denominator, frinNum1.denominator * frinNum2.denominator);
	frRet = fractionSimplify(frRet);

	return(frRet);
}

/*
 *	fact fractionMultiplination(fact frNum1, fact frNum2);
 *
 *	To get the multiply of two fractions (frNum1 & frNum2).
 *
 *	frNum1	->	Fraction one,
 *	frNum2	->	Fraction two.
 */
fact fractionMultiplination(fact frNum1, fact frNum2)
{
	fact frRet;
	fact frinNum1, frinNum2;

	frinNum1 = frNum1;
	frinNum2 = frNum2;

	if (frinNum1.numerator == 0) {
		frinNum1.denominator = 1;
	}

	if (frinNum2.numerator == 0) {
		frinNum2.denominator = 1;
	}

	frRet = fractionCreate(frinNum1.numerator * frinNum2.numerator, frinNum1.denominator * frinNum2.denominator);
	frRet = fractionSimplify(frRet);

	return(frRet);
}

/*
 *	fact fractionDivision(fact frNum1, fact frNum2);
 *
 *	To get the divide of two fractions (frNum1 & frNum2).
 *
 *	frNum1	->	Fraction one,
 *	frNum2	->	Fraction two.
 */
fact fractionDivision(fact frNum1, fact frNum2)
{
	fact frRet;
	fact frinNum1, frinNum2;

	frinNum1 = frNum1;
	frinNum2 = frNum2;

	if (frinNum1.numerator == 0) {
		frinNum1.denominator = 1;
	}
	if (frinNum2.numerator == 0) {
		frinNum2.denominator = 1;
	}

	frRet = fractionCreate(frinNum1.numerator * frinNum2.denominator, frinNum1.denominator * frinNum2.numerator);
	frRet = fractionSimplify(frRet);

	return(frRet);
}

/*
 *	long double* fractionToNumber(fact *frStack, int lnLen);
 *
 *	Converts a fraction heap to long double heap.
 *
 *	*frStack	->	the fraction heap,
 *	lnLen	->	the length of the fraction heap.
 */
long double* fractionToNumber(fact *frStack, int lnLen)
{
	long double *ldRet;
	long double ld_mmul;
	int idx;

	if (frStack == NULL) {
		return(NULL);
	}

	ldRet = (long double*) malloc(lnLen * sizeof(long double));
	if (ldRet == NULL) {
		return(NULL);
	}

	ld_mmul = 1;

	for (idx = 1; idx <= lnLen; idx++) {
		*(frStack + (idx - 1)) = fractionSimplify(*(frStack + (idx - 1)));
	}

	for (idx = 1; idx <= lnLen; idx++) {
		ld_mmul = mmul(ld_mmul, (frStack + (idx - 1))->denominator);
	}

	for (idx = 1; idx <= lnLen; idx++) {
		*(ldRet + (idx - 1)) = (frStack + (idx - 1))->numerator * ld_mmul / (frStack + (idx - 1))->denominator;
	}

	return(ldRet);
}

/*
 *	int fractionCompare(fact x, fact y);
 *
 *	To compare two fractions.
 *
 *	x		->	Fraction one,
 *	y		->	Fraction two.
 */
int fractionCompare(fact x, fact y)
{
	long double ln_mmul;
	fact ps1, ps2;

	ps1 = fractionSimplify(x);
	ps2 = fractionSimplify(y);

	ln_mmul = mmul(ps1.denominator, ps2.denominator);

	ps1.numerator = ps1.numerator * ln_mmul / ps1.denominator;
	ps1.denominator = ln_mmul;
	ps2.numerator = ps2.numerator * ln_mmul / ps2.denominator;
	ps2.denominator = ln_mmul;

	if (ps1.numerator > ps2.numerator) {
		return(-1);
	} else if (ps1.numerator == ps2.numerator) {
		return(0);
	} else if (ps1.numerator < ps2.numerator) {
		return(1);
	}

	return(-2);
}

